home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2009 February / PCWFEB09.iso / Software / Resources / Audio, Video & Photo / Songbird 0.7.0 / Songbird_0.7.0_windows-i686-msvc8.exe / components / sbDisplayPanes.js < prev    next >
Text File  |  2008-08-06  |  17KB  |  524 lines

  1. //
  2. // BEGIN SONGBIRD GPL
  3. // 
  4. // This file is part of the Songbird web player.
  5. //
  6. // Copyright(c) 2005-2008 POTI, Inc.
  7. // http://songbirdnest.com
  8. // 
  9. // This file may be licensed under the terms of of the
  10. // GNU General Public License Version 2 (the "GPL").
  11. // 
  12. // Software distributed under the License is distributed 
  13. // on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either 
  14. // express or implied. See the GPL for the specific language 
  15. // governing rights and limitations.
  16. //
  17. // You should have received a copy of the GPL along with this 
  18. // program. If not, go to http://www.gnu.org/licenses/gpl.html
  19. // or write to the Free Software Foundation, Inc., 
  20. // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  21. // 
  22. // END SONGBIRD GPL
  23. //
  24.  
  25. Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
  26. Components.utils.import("resource://app/jsmodules/ArrayConverter.jsm");
  27. Components.utils.import("resource://app/jsmodules/RDFHelper.jsm");
  28.  
  29. /**
  30.  * sbIContentPaneInfo
  31.  */
  32.  
  33. function PaneInfo() {};
  34.  
  35. PaneInfo.prototype = {
  36.   requiredProperties: [ "contentUrl", 
  37.                         "contentTitle",
  38.                         "contentIcon",
  39.                         "suggestedContentGroups",
  40.                         "defaultWidth", 
  41.                         "defaultHeight" ],
  42.   optionalProperties: [ "showOnInstall" ],
  43.     
  44.   verify: function() {
  45.     var errorList = [];
  46.     for (var i = 0; i < this.requiredProperties.length; i++) {
  47.       var property = this.requiredProperties[i];
  48.       if (! (typeof(this[property]) == 'string'
  49.                && this[property].length > 0)) 
  50.       {
  51.         errorList.push("Invalid description. '" + property + "' is a required property.");
  52.       }
  53.     }
  54.     try {
  55.       this.defaultWidth = parseInt(this.defaultWidth);
  56.       this.defaultHeight = parseInt(this.defaultHeight);
  57.       this.showOnInstall = this.showOnInstall == "true";
  58.     } catch (e) {
  59.       errorList.push(e.toString());
  60.     }
  61.     return(errorList);
  62.   },
  63.   
  64.   QueryInterface: function(iid) {
  65.     if (!iid.equals(Components.interfaces.sbIDisplayPaneContentInfo)) 
  66.       throw Components.results.NS_ERROR_NO_INTERFACE;
  67.     return this;
  68.   }
  69. };
  70.  
  71. /**
  72.  * /class DisplayPaneMetadataReader
  73.  * Responsible for reading addon metadata and performing 
  74.  * registration with DisplayPaneManager
  75.  */
  76. function DisplayPaneMetadataReader() {
  77.   //debug("DisplayPaneMetadataReader: ctor\n");
  78.   this._manager = Components.classes["@songbirdnest.com/Songbird/DisplayPane/Manager;1"]
  79.                             .getService(Components.interfaces.sbIDisplayPaneManager);
  80. }
  81. DisplayPaneMetadataReader.prototype = {
  82.   _manager: null,
  83.  
  84.   /**
  85.    * Populate DisplayPaneManager using addon metadata
  86.    */
  87.   loadPanes: function loadPanes() {
  88.     //debug("DisplayPaneMetadataReader: loadPanes\n");
  89.     var addons = RDFHelper.help("rdf:addon-metadata", "urn:songbird:addon:root", RDFHelper.DEFAULT_RDF_NAMESPACES);
  90.     
  91.     for (var i = 0; i < addons.length; i++) {
  92.       // skip addons with no panes.
  93.       var panes;
  94.       if (addons[i].displayPanes) {
  95.         // TODO: remove this some time post 0.5 and before 1.0
  96.         Components.utils.reportError(
  97.           "DisplayPanes: Use of the <displayPanes> element in install.rdf " +
  98.           "is deprecated. Remove that element and leave the contents as-is."
  99.         );
  100.         panes = addons[i].displayPanes[0].displayPane;
  101.       }
  102.       else {
  103.         panes = addons[i].displayPane;
  104.       }
  105.       
  106.       if (!panes) {
  107.         // no display panes in this addon.
  108.         continue;
  109.       }
  110.       
  111.       try {
  112.         for (var j = 0; j < panes.length; j++) {
  113.           this._registerDisplayPane(addons[i], panes[j]) 
  114.         }
  115.       } catch (e) {
  116.         this._reportErrors("", [  "An error occurred while processing " +
  117.                   "extension " + addons[i].Value + ".  Exception: " + e  ]);
  118.       }
  119.     }
  120.   },
  121.   
  122.   /**
  123.    * Extract pane metadata and register it with the manager.
  124.    */
  125.   _registerDisplayPane: function _registerDisplayPane(addon, pane) {
  126.     // create and validate our pane info
  127.     var info = new PaneInfo();
  128.     for (property in pane) {
  129.       if (pane[property])
  130.        info[property] = pane[property][0];
  131.     }
  132.     var errorList = info.verify();
  133.     
  134.     // If errors were encountered, then do not submit 
  135.     // to the Display Pane Manager
  136.     if (errorList.length > 0) {
  137.       this._reportErrors(
  138.           "Ignoring display pane addon in the install.rdf of extension " +
  139.           addon.Value + " due to these error(s):\n", errorList);
  140.       return;
  141.     }
  142.     
  143.     // Submit description
  144.     this._manager.registerContent( info.contentUrl, 
  145.                                    info.contentTitle,
  146.                                    info.contentIcon,
  147.                                    info.defaultWidth,
  148.                                    info.defaultHeight,
  149.                                    info.suggestedContentGroups,
  150.                                    info.showOnInstall );
  151.     
  152.     //debug("DisplayPaneMetadataReader: registered pane " + info.contentTitle
  153.     //        + " from addon " + addon.Value + " \n");
  154.   },
  155.   
  156.   
  157.   /**
  158.    * \brief Dump a list of errors to the console and jsconsole
  159.    *
  160.    * \param contextMessage Additional prefix to use before every line
  161.    * \param errorList Array of error messages
  162.    */
  163.   _reportErrors: function _reportErrors(contextMessage, errorList) {
  164.     var consoleService = Components.classes["@mozilla.org/consoleservice;1"].
  165.          getService(Components.interfaces.nsIConsoleService);
  166.     for (var i = 0; i  < errorList.length; i++) {
  167.       consoleService.logStringMessage("Display Pane Metadata Reader: " 
  168.                                        + contextMessage + errorList[i]);
  169.       dump("DisplayPaneMetadataReader: " + contextMessage + errorList[i] + "\n");
  170.     }
  171.   }
  172. }
  173.  
  174. /**
  175.  * /class DisplayPaneManager
  176.  * /brief Coordinates display pane content
  177.  *
  178.  * Acts as a registry for display panes and available content.
  179.  *
  180.  * \sa sbIDisplayPaneManager
  181.  */
  182. function DisplayPaneManager() {
  183. }
  184.  
  185. DisplayPaneManager.prototype.constructor = DisplayPaneManager;
  186.  
  187. DisplayPaneManager.prototype = {
  188.   classDescription: "Songbird Display Pane Manager Service Interface",
  189.   classID:          Components.ID("{6aef120f-d7ad-414d-a93d-3ac945e64301}"),
  190.   contractID:       "@songbirdnest.com/Songbird/DisplayPane/Manager;1",
  191.  
  192.   LOG: function(str) {
  193.     var consoleService = Components.classes['@mozilla.org/consoleservice;1']
  194.                             .getService(Components.interfaces.nsIConsoleService);
  195.     consoleService.logStringMessage(str);
  196.   },
  197.  
  198.   _contentList: [],
  199.   _instantiatorsList: [],
  200.   _delayedInstantiations: [],
  201.   _listenersList: [],
  202.   
  203.   _addonMetadataLoaded: false,
  204.  
  205.   _getString: function(aName, aDefault) {
  206.     if (!this._stringbundle) {
  207.       var src = "chrome://branding/locale/brand.properties";
  208.       var stringBundleService = Components.classes["@mozilla.org/intl/stringbundle;1"]
  209.                                   .getService(Components.interfaces.nsIStringBundleService);
  210.       this._stringbundle = stringBundleService.createBundle(src);
  211.     }
  212.         
  213.     try {
  214.       return this._stringbundle.GetStringFromName(aName);
  215.     }
  216.     catch(e) {
  217.       return aDefault;
  218.     }
  219.  
  220.   },
  221.   
  222.   _defaultPaneInfo: null,
  223.   
  224.   get defaultPaneInfo() {
  225.     if (!this._defaultPaneInfo) {
  226.       var paneInfo = {
  227.         contentUrl: this._getString("displaypane.default.url",  "chrome://songbird/content/xul/defaultDisplayPane.xul"),
  228.         contentTitle: null, // automatically set by host depending on where the default pane is instantiated
  229.         contentIcon: this._getString("displaypane.default.icon",  "http://songbirdnest.com/favicon.ico"),
  230.         defaultWidth: this._getString("displaypane.default.width",  "150"),
  231.         defaultHeight: this._getString("displaypane.default.height",  "75"),
  232.         suggestedContentGroups: this._getString("displaypane.default.groups",  "")
  233.       };
  234.       this._defaultPaneInfo = paneInfo;
  235.     }
  236.     return this._defaultPaneInfo;
  237.   },
  238.  
  239.   /**
  240.    * Make sure that we've read display pane registration
  241.    * metadata from all extension install.rdf files.
  242.    */
  243.   ensureAddonMetadataLoaded: function() {
  244.     if (this._addonMetadataLoaded) {
  245.       return;
  246.     }
  247.     this._addonMetadataLoaded = true;
  248.     
  249.     // Load the addon metadata
  250.     var metadataReader = new DisplayPaneMetadataReader();
  251.     metadataReader.loadPanes();
  252.   },
  253.   
  254.  
  255.   /**
  256.    * given a list of pane parameters, return a new sbIDisplayPaneContentInfo
  257.    */
  258.   makePaneInfo: function(aContentUrl,
  259.                          aContentTitle,
  260.                          aContentIcon,
  261.                          aSuggestedContentGroups,
  262.                          aDefaultWidth,
  263.                          aDefaultHeight) {
  264.     var paneInfo = new PaneInfo();
  265.     paneInfo.contentUrl = aContentUrl;
  266.     paneInfo.contentTitle = aContentTitle;
  267.     paneInfo.contentIcon = aContentIcon;
  268.     paneInfo.suggestedContentGroups = aSuggestedContentGroups;
  269.     paneInfo.defaultWidth = aDefaultWidth;
  270.     paneInfo.defaultHeight = aDefaultHeight;
  271.     
  272.     return paneInfo;
  273.   },
  274.  
  275.   /**
  276.    * \see sbIDisplayPaneManager
  277.    */
  278.   getPaneInfo: function(aContentUrl) {
  279.     this.ensureAddonMetadataLoaded();
  280.   
  281.     for each (var pane in this._contentList) {
  282.       ////debug("PANE: " + pane.contentTitle  + " XXX " + aContentUrl + "\n\n");
  283.       if (pane.contentUrl == aContentUrl) 
  284.         return pane;
  285.     }
  286.     return null;
  287.   },
  288.  
  289.   /**
  290.    * \see sbIDisplayPaneManager
  291.    */
  292.   get contentList() {
  293.     this.ensureAddonMetadataLoaded();
  294.     return ArrayConverter.enumerator(this._contentList);
  295.   },
  296.   
  297.   /**
  298.    * \see sbIDisplayPaneManager
  299.    */
  300.   get instantiatorsList() {
  301.     for (var i = this._instantiatorsList.length - 1; i >= 0; --i) {
  302.       if (!(this._instantiatorsList[i] instanceof
  303.             Components.interfaces.sbIDisplayPaneInstantiator)) {
  304.         Components.utils.reportError("Warning: found bad instantiator; "+
  305.                                      "possibly via removal from DOM");
  306.         this._instantiatorsList.splice(i, 1);
  307.       }
  308.     }
  309.     return ArrayConverter.enumerator(this._instantiatorsList);
  310.   },
  311.   
  312.   /**
  313.    * \see sbIDisplayPaneManager
  314.    */
  315.   registerContent: function(aContentUrl,
  316.                             aContentTitle,
  317.                             aContentIcon,
  318.                             aDefaultWidth,
  319.                             aDefaultHeight,
  320.                             aSuggestedContentGroups,
  321.                             aAutoShow) {
  322.                             
  323.     ////debug("REGISTER: " + aContentUrl + "\n");
  324.     
  325.     var info = this.getPaneInfo(aContentUrl);
  326.     if (info) {
  327.       throw Components.results.NS_ERROR_ALREADY_INITIALIZED;
  328.     }
  329.     info = this.makePaneInfo(aContentUrl,
  330.                              aContentTitle,
  331.                              aContentIcon,
  332.                              aSuggestedContentGroups,
  333.                              aDefaultWidth,
  334.                              aDefaultHeight);
  335.     this._contentList.push(info);
  336.     for each (var listener in this._listenersList) {
  337.       listener.onRegisterContent(info);
  338.     }
  339.     // if we have never seen this pane, show it in its prefered group
  340.     var SB_NewDataRemote = Components.Constructor("@songbirdnest.com/Songbird/DataRemote;1",
  341.                                                   "sbIDataRemote",
  342.                                                   "init");
  343.     var known = SB_NewDataRemote("displaypane.known." + aContentUrl, null);
  344.     if (!known.boolValue) {
  345.       if (aAutoShow) {
  346.         if (!this.tryInstantiation(info)) {
  347.           this._delayedInstantiations.push(info);
  348.         }
  349.       }
  350.       // remember we've seen this pane, let the pane hosts reload on their own if they need to
  351.       known.boolValue = true;
  352.     }
  353.   },
  354.   
  355.   /**
  356.    * \see sbIDisplayPaneManager
  357.    */
  358.   unregisterContent: function(aContentUrl) {
  359.     for (var contentIndex = 0; contentIndex < this._contentList.length; contentIndex++) {
  360.       if (this._contentList[contentIndex].contentUrl != aContentUrl) {
  361.         continue;
  362.       }
  363.  
  364.       // any instantiator currently hosting this url should be emptied
  365.       for each (var instantiator in this._instantiatorsList) {
  366.         if (instantiator.contentUrl == aContentUrl) {
  367.           instantiator.hide();
  368.         }
  369.       }
  370.       // also remove it from the delayed instantiation list
  371.       for (instantiatorIndex = this._delayedInstantiations.length - 1; instantiatorIndex >= 0; --instantiatorIndex) {
  372.         if (this._delayedInstantiations[instantiatorIndex].contentUrl == aContentUrl) {
  373.           this._delayedInstantiations.splice(instantiatorIndex, 1);
  374.         }
  375.       }
  376.  
  377.       var [info] = this._contentList.splice(contentIndex, 1);
  378.       
  379.       for each (var listener in this._listenersList) {
  380.         listener.onUnregisterContent(info);
  381.       }
  382.       return;
  383.     }
  384.   },
  385.   
  386.   /**
  387.    * \see sbIDisplayPaneManager
  388.    */
  389.   registerInstantiator: function(aInstantiator) {
  390.     this.ensureAddonMetadataLoaded();
  391.     
  392.     if (this._instantiatorsList.indexOf(aInstantiator) > -1) {
  393.       Components.utils.reportError("Attempt to re-register instantiator ignored\n" +
  394.                                    (new Error()).stack);
  395.       return;
  396.     }
  397.     this._instantiatorsList.push(aInstantiator);
  398.     for each (var listener in this._listenersList) {
  399.       listener.onRegisterInstantiator(aInstantiator);
  400.     }
  401.     this.processDelayedInstantiations();
  402.   },
  403.  
  404.   /**
  405.    * \see sbIDisplayPaneManager
  406.    */
  407.   unregisterInstantiator: function(aInstantiator) {
  408.     var index = this._instantiatorsList.indexOf(aInstantiator);
  409.     if (index < 0) {
  410.       // not found
  411.       return;
  412.     }
  413.     this._instantiatorsList.splice(index, 1);
  414.     for each (var listener in this._listenersList) {
  415.       listener.onUnregisterInstantiator(aInstantiator);
  416.     }
  417.   },
  418.   
  419.   /**
  420.    * given a content group list (from a sbIDisplayPaneContentInfo),
  421.    * return the the first instantiator that matches the earliest possible
  422.    * content group
  423.    */
  424.   getFirstInstantiatorForGroupList: function(aContentGroupList) {
  425.     var groups = aContentGroupList.toUpperCase().split(";");
  426.     for each (var group in groups) {
  427.       for each (var instantiator in this._instantiatorsList) {
  428.         if (instantiator.contentGroup.toUpperCase() == group) {
  429.           return instantiator;
  430.         }
  431.       }
  432.     }
  433.     return null;
  434.   },
  435.   
  436.   processDelayedInstantiations: function() {
  437.     var table = [];
  438.     for each (var info in this._delayedInstantiations) {
  439.       if (!this.isValidPane(info) || this.tryInstantiation(info)) {
  440.         continue;
  441.       }
  442.       table.push(info);
  443.     }
  444.     this._delayedInstantiations = table;
  445.   },
  446.   
  447.   tryInstantiation: function(info) {
  448.     var instantiator = this.getFirstInstantiatorForGroupList(info.suggestedContentGroups);
  449.     if (instantiator) {
  450.       instantiator.loadContent(info);
  451.       return true;
  452.     }
  453.     return false;
  454.   },
  455.   
  456.   isValidPane: function(aPane) {
  457.     this.ensureAddonMetadataLoaded();
  458.     for each (var pane in this._contentList) {
  459.       if (pane == aPane) return true;
  460.     }
  461.     return false;
  462.   },
  463.  
  464.   showPane: function(aContentUrl) {
  465.     for each (var instantiator in this._instantiatorsList) {
  466.       if (instantiator.contentUrl == aContentUrl) {
  467.         // we already have a pane with this content
  468.         instantiator.collapsed = false;
  469.         return;
  470.       }
  471.     }
  472.     var info = this.getPaneInfo(aContentUrl);
  473.     if (info) {
  474.       if (!this.tryInstantiation(info)) {
  475.         this._delayedInstantiations.push(info);
  476.       }
  477.     } else {
  478.       throw new Error("Content URL was not found in list of registered panes");
  479.     }
  480.   },
  481.   
  482.   addListener: function(aListener) {
  483.     this._listenersList.push(aListener);
  484.   },
  485.   
  486.   removeListener: function(aListener) {
  487.     var index = this._listenersList.indexOf(aListener);
  488.     if (index > -1)
  489.       this._listenersList.splice(index, 1);
  490.   },
  491.   
  492.   updateContentInfo: function(aContentUrl, aNewContentTitle, aNewContentIcon) {
  493.     var info = this.getPaneInfo(aContentUrl);
  494.     if (!info) {
  495.       throw Components.results.NS_ERROR_NOT_INITIALIZED;
  496.     }
  497.  
  498.     info.contentTitle = aNewContentTitle;
  499.     info.contentIcon  = aNewContentIcon;
  500.     
  501.     // change the live title for every instance of this content
  502.     for each (var instantiator in this._instantiatorsList) {
  503.       if (instantiator.contentUrl == aContentUrl) {
  504.         instantiator.contentTitle = aNewContentTitle;
  505.         instantiator.contentIcon = aNewContentIcon;
  506.       }
  507.     }
  508.     for each (var listener in this._listenersList) {
  509.       listener.onPaneInfoChanged(info);
  510.     }
  511.   },
  512.  
  513.   /**
  514.    * \see nsISupports.idl
  515.    */
  516.   QueryInterface:
  517.     XPCOMUtils.generateQI([Components.interfaces.sbIDisplayPaneManager])
  518. }; // DisplayPaneManager.prototype
  519.  
  520. function NSGetModule(compMgr, fileSpec) {
  521.   return XPCOMUtils.generateModule([DisplayPaneManager]);
  522. }
  523.  
  524.